#include <stdlib.h>
#include <iostream>
#include <vector>
#include <string>
#include <fstream>

#include "SQLGateway.hpp"

#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>


#define MAX_QUOTES 200

namespace po = boost::program_options;

// Typdefs
typedef std::vector<std::string> VectorString;

// Prototypes
bool testQuote(std::vector<std::string> vecSymbols, bool bStoreInSQL, VerboseOptions verboseOptions);
bool testDiscover(std::string strURL);
bool testDescribe(std::string strTableName);
std::string get_file_contents(const char *filename);

int main(int argc, char** argv) 
{ 
  VectorString vecString;
  VerboseOptions verboseOptions = VerboseOptions::None;
  bool bStoreInSQL = false;
  
  
 
  
    
  try {
    po::options_description desc("Allowed options");
    desc.add_options()
    ("help", "produce help message")
    ("filequote",   po::value<std::string>(), "Load Quotes from File")
    ("quote",     po::value<VectorString>(&vecString), "Fetch Quote")
    ("hist",      po::value<VectorString>(&vecString), "Get History")
    ("discquotes", "Discover Quotes")
    ("sqldescribe", po::value<std::string>(), "Describe SQL Table")
    ("sqlinsert",   "Insert into SQL Table")
    ("v",   "Verbose Output")           // VerboseOptions::Low
    ("vv",  "Extra Verbose Output")     // VerboseOptions::High
    ("vvv", "Debugging Output")         // VerboseOptions::Debug
    ;
    
    po::variables_map vm;        
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);      
    
    
    ///////////////////////////////////////////
    // Help
    if (vm.count("help")) {
      std::cout << desc << "\n";
      return EXIT_SUCCESS;
    }
    
    
    ////////////////////////////////////////////
    // Verbosity
    if (vm.count("v")) {   // Must be at top
      verboseOptions = VerboseOptions::Low;
    }
    if (vm.count("vv")) {
      verboseOptions = VerboseOptions::High;
    }
    if (vm.count("vvv")) {
      verboseOptions = VerboseOptions::Debug;
    }
    
    
    /////////////////////////////////////////////
    // Modifiers
    if (vm.count("sqlinsert")) {   // Must be at top
      bStoreInSQL = true;
    }
    
    
    /////////////////////////////////////////////
    // Commands - Only one 
    
    // -quote <symbol> -quote <symbol> ... 
    // Retreive quote data for one or more symbols
    if (vm.count("quote")) {
      if (vm.count("hist") || vm.count("filequote") || vm.count("discquotes") || vm.count("sqldescribe")) {
	std::cout << "Error: 'quote' cannot be combined with other commands.\n";
	return EXIT_FAILURE;
      }
      
      if (vecString.empty()) {
	std::cout << "Error: 'quote' requires a symbol.\n";
	return EXIT_FAILURE;
      }
      
      testQuote(vecString, bStoreInSQL, verboseOptions);  
      return EXIT_SUCCESS;
    }
    
    
    // -filequote <filename>
    // filename is one symbol per line, load quotes for all symbols in file
    
    if (vm.count("filequote")) {
      if (vm.count("hist") || vm.count("quote") || vm.count("discquotes") || vm.count("sqldescribe")) {
	std::cout << "Error: 'filequote' cannot be combined with other commands.\n";
	return EXIT_FAILURE;
      }
      
      try {
	std::string strFileQuote = vm["filequote"].as<std::string>();
	std::ifstream file(strFileQuote);
	std::string strLine;
	VectorString vecLines;
	while (std::getline(file, strLine)) {
	  boost::algorithm::trim(strLine);
	  vecLines.push_back(strLine);
	  if (vecLines.size() >= MAX_QUOTES) {
	    testQuote(vecLines, bStoreInSQL, verboseOptions);
	    vecLines.clear();
	  }
	}
	// Flush out any leftovers
	if (vecLines.size() > 0) {   
	  testQuote(vecLines, bStoreInSQL, verboseOptions);		
	}
	
      } catch (...) { 
	std::cout << "Error: Could not open '" << vm["filequote"].as<std::string>() << "'\n";
	return EXIT_FAILURE;
      }
      
      return EXIT_SUCCESS;
    } // filequote
    
    
    // Retreives the structure of yahoo.finance.quotes, and produces an SQL insert statement to create a table that can hold the data.
    // If fieldnames are recognized by FieldNameLookup the correct column type is set, otherwise defaults to the sql type 'text'
    if (vm.count("discquotes"))
    {
      if (vm.count("hist") || vm.count("quote") || vm.count("filequote") || vm.count("sqldescribe")) {
	std::cout << "Error: 'discquotes' cannot be combined with other commands.\n";
	return EXIT_FAILURE;
      }
      
      std::string strUrl = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.quotes%20where%20symbol%20in%20%28%22AAPL%22%29&env=store://datatables.org/alltableswithkeys";
      
      testDiscover(strUrl);
      return EXIT_SUCCESS;
    } // discquotes
    
    
    // Executes mysql statement "DESCRIBE <tablename>" and returns the schema.
    // -sqldescribe <tablename>
    if (vm.count("sqldescribe"))
    {
      if (vm.count("hist") || vm.count("quote") || vm.count("filequote") || vm.count("discquotes")) {
	std::cout << "Error: 'sqldescribe' cannot be combined with other commands.\n";
	return EXIT_FAILURE;
      }
      
      testDescribe(vm["sqldescribe"].as<std::string>());  
      return EXIT_SUCCESS;
    }

    ///////////////////////////////////////////////////
    // FINAL
    // If we get to here, there were no commands executed. Dump the help.
    std::cout << desc << "\n";
    
  }
  catch(std::exception& e) {
    std::cerr << "Error: " << e.what() << "\n";
    return EXIT_FAILURE;
  }
  catch(...) {
    std::cerr << "Exception of unknown type!\n";
  }
  
  
  
  return EXIT_SUCCESS;
} 




bool testDescribe(std::string strTableName)
{
  SQLGateway sql;
  
  if (!sql.Connect()) {
    std::cout << "testDescribe() - Error connecting\n  " << sql.getConnectionErrorMessage() << "\n";
    return false;
  }
  
  SetString s;
  if (!sql.getSQLTableFieldNames("tableName", s)) {
    std::cout << "testDescribe() - Error getting field names\n  " 
    << sql.getLastSQLQuery() << "\n  "
    << sql.getLastSQLError() << "\n";
    return false;
  }
  
  std::cout << "Field Names\n---------\n";  
  
  for (SetString::const_iterator itr = s.begin(); itr != s.end(); ++itr)
  {
    std::cout << *itr << "\n";   
  }
  
  return true;
}



std::string get_file_contents(const char *filename)
{
  std::ifstream in(filename, std::ios::in | std::ios::binary);
  if (in)
  {
    std::string contents;
    in.seekg(0, std::ios::end);
    contents.resize(in.tellg());
    in.seekg(0, std::ios::beg);
    in.read(&contents[0], contents.size());
    in.close();
    return(contents);
  }
  throw(errno);
}
